<?php


namespace app\xiangji\services;


use app\common\caches\UserSignCache;
use app\common\facades\auth\Auth;
use app\common\models\LoveAchieve;
use app\Request;
use app\common\helpers\Response;
use app\common\models\UserSign;
use app\xiangji\exception\BadRequestException;
use app\xiangji\exception\ServiceUnavailableException;
use app\xiangji\jobs\AchieveLoveScoreJob;
use app\xiangji\jobs\GiveLoveScoreJob;
use Carbon\Carbon;
use think\facade\Db;
use think\facade\Queue;

class UserSignService
{
    const SINGLE_SIGN_REWARDS = 1; // 单人签到奖励值
    const DOUBLE_SIGN_REWARDS = 2; // 双人签到奖励值

    private $user;
    private $signModel;

    public function __construct(UserSign $signModel)
    {
        $this->user = Auth::guard('api')->user();
        $this->signModel = $signModel;
    }

    public function hug1hug()
    {
        $today = Carbon::now()->format('Y-m-d');
        // 1.判断是否今日已双方都亲过了
        if (UserSignCache::getKissStatus($this->user->id) != $today || UserSignCache::getKissStatus($this->user->id) != UserSignCache::getKissStatus($this->user->lover->lover_id)){
            throw new BadRequestException('互相亲一亲任务还未完成');
        }
        $user_sign = $this->user->signs()->where('sign_date', '=', $today)->find();
        // 4.更新redis数据，插入本人亲亲
        UserSignCache::setHugStatus($this->user->id, $today);
        $hug_status = 1;
        // 5.如双方都完成，发放奖励（奖励队列发放）
        if (UserSignCache::getHugStatus($this->user->lover->lover_id) == $today){
            $hug_status = 2;
            // 发放奖励（队列）
            Queue::push(GiveLoveScoreJob::class, ['user_id' => $this->user->id, 'score' => 5, 'scene' => '抱一抱奖励'], 'give_love_score');
            Queue::push(GiveLoveScoreJob::class, ['user_id' => $this->user->lover->lover_id, 'score' => 5, 'scene' => '抱一抱奖励'], 'give_love_score');
        }
        // 记录数据库
        $user_sign->save(['hug_status' => 1]);
        return ['hug_status' => $hug_status];
    }

    public function kiss1kiss()
    {
        $today = Carbon::now()->format('Y-m-d');
        // 1.判断是否今日已亲过了
        if (UserSignCache::getKissStatus($this->user->id) == $today){
            throw new BadRequestException('今日已亲过Ta了');
        }
        // 2.判断是否已绑定恋人
        if ($this->user->lover->lover_id <= 0){
            throw new BadRequestException('未绑定恋人');
        }
        // 3.判断是否双方已完成签到(即：今日签到状态是否为2)
        $user_sign = $this->user->signs()->where('sign_date', '=', $today)->find();
        if (!$user_sign || $user_sign->sign_status != UserSign::DOUBLE_SIGN){
            throw new BadRequestException('未完成双方互想任务');
        }
        // 4.更新redis数据，插入本人亲亲
        UserSignCache::setKissStatus($this->user->id, $today);
        $kiss_status = 1;
        // 5.如双方都完成，发放奖励（奖励队列发放）
        if (UserSignCache::getKissStatus($this->user->lover->lover_id) == $today){
            $kiss_status = 2;
            // 发放奖励（队列）
            Queue::push(GiveLoveScoreJob::class, ['user_id' => $this->user->id, 'score' => 2, 'scene' => '亲一亲奖励'], 'give_love_score');
            Queue::push(GiveLoveScoreJob::class, ['user_id' => $this->user->lover->lover_id, 'score' => 2, 'scene' => '亲一亲奖励'], 'give_love_score');
        }
        // 记录数据库
        $user_sign->save(['kiss_status' => 1]);
        return ['kiss_status' => $kiss_status];
    }

    /**
     * @todo 每日签到，分单人签到和双人签到
     * 未绑定恋人获得单倍奖励
     * 绑定恋人但恋人未签到获得单倍奖励
     * 绑定恋人且恋人已签到获得双倍奖励
     * @return int[]
     * @throws \GuzzleHttp\Exception\InvalidArgumentException
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function dailySign()
    {
        $today = Carbon::now()->format('Y-m-d');
        // 用户今日签到情况
        $user_sign = $this->user->signs()->where('sign_date', '=', $today)->find();
        if ($user_sign && $user_sign->sign_status > UserSign::NOT_SIGN){
            // 用户今日已签到
            throw new BadRequestException('今日已签到，不能再次签到');
        }
        // 判断用户恩爱值账户是否正常
        if (!LoveScoreService::checkLoveScoreSafety($this->user->loveScore)){
            throw new BadRequestException('账户异常', Response::HTTP_FORBIDDEN);
        }
        // 奖励信息
        $user_sign_rewards = static::SINGLE_SIGN_REWARDS;
        // 判断是否有恋人，恋人签到状态
        $lover = UserService::getUserLover($this->user);
        if ($lover){
            // 用户恋人今日签到情况
            $lover_sign = $lover->signs()->where('sign_date', '=', $today)->find();
            // 恋人今日已签到
            if ($lover_sign && $lover_sign->sign_status == 1){
                $user_sign_rewards += static::DOUBLE_SIGN_REWARDS - static::SINGLE_SIGN_REWARDS;
                $lover_sign_add_rewards = static::DOUBLE_SIGN_REWARDS - static::SINGLE_SIGN_REWARDS;
            }
        }
        // 签到状态值
        $sign_status = isset($lover_sign_add_rewards) ? UserSign::DOUBLE_SIGN : UserSign::SINGLE_SIGN;
        // 签到操作
        Db::startTrans();
        try {
            // 保存本人今日签到数据
            $this->user->signs()->save(['sign_date' => $today, 'sign_status' => $sign_status]);
            // 更新恋人今日签到数据库
            if ($sign_status == UserSign::DOUBLE_SIGN){
                $lover_sign->save(['sign_status' => $sign_status]);
            }
            Db::commit();
            // 发放奖励
            Queue::push(GiveLoveScoreJob::class, ['user_id' => $this->user->id, 'score' => $user_sign_rewards, 'scene' => '签到奖励'], 'give_love_score');
            if ($sign_status == UserSign::DOUBLE_SIGN){
                Queue::push(GiveLoveScoreJob::class, ['user_id' => $lover->id, 'score' => $lover_sign_add_rewards, 'scene' => '恋人签到,追加奖励'], 'give_love_score');
            }
            // 触发成就
            Queue::push(AchieveLoveScoreJob::class, ['user_id' => $this->user->id, 'type' => LoveAchieve::TYPE_SIGN], 'love_score_achieve');
        } catch (\Exception $e){
            Db::rollback();
            throw new ServiceUnavailableException('系统错误，请稍后再试');
        }
        // 返回本次签到状态，由前端自行处理
        return [
            'sign_status' => $sign_status
        ];
    }

    /**
     * @todo 获取连续签到天数：今日或昨日起连续签到的天数
     * @return mixed
     */
    public function continueSignDays()
    {
        // 从缓存中获取数据
        $today = Carbon::today()->format('Y-m-d');
        $user_id = $this->user->id;
        $user_days = UserSignCache::getUserContinueSignDays($user_id, $today);
        if (is_null($user_days)){
            $user_days = $this->queryUserContinueSignDays($user_id);
            UserSignCache::setUserContinueSignDays($user_id, $today, $user_days);
        }
        $data = ['self' => $user_days];
        // 如果存在恋人
        if ($this->user->lover){
            $lover_id = $this->user->lover->lover_id;
            $lover_days = UserSignCache::getUserContinueSignDays($lover_id, $today);
            if (is_null($lover_days)) {
                $lover_days = $this->queryUserContinueSignDays($lover_id);
                UserSignCache::setUserContinueSignDays($lover_id, $today, $lover_days);
            }
            $data['lover'] = $lover_days;
        }
        return $data;
    }

    /**
     * @todo 查询用户连续签到天数
     * @param int $user_id  用户ID
     * @return mixed
     */
    private function queryUserContinueSignDays(int $user_id)
    {
        $sql = <<<EOT
                SELECT
                    count(1) as days
                FROM
                    ( SELECT
                        date_sub(a.sign_date, INTERVAL 1 DAY) sign_date,
                        ( @i := DATE_ADD(@i, INTERVAL - 1 DAY) ) today 
                    FROM
                        xiangji_user_signs a 
                    INNER JOIN
                        (
                            SELECT
                                @i := max(sign_date) 
                            FROM
                                xiangji_user_signs 
                            WHERE
                                user_id = {$user_id}
                                AND TO_DAYS(sign_date) = TO_DAYS(curdate()) 
                                OR TO_DAYS(sign_date) = TO_DAYS( DATE_ADD(curdate(), INTERVAL - 1 DAY) ) 
                        ) b 
                    WHERE
                        a.user_id = {$user_id}
                        AND a.sign_status > 0
                    ORDER BY
                        a.sign_date DESC ) c 
                    WHERE
                        TO_DAYS(today) = TO_DAYS(sign_date)
EOT;
        $data = Db::query($sql);
        return $data[0];
    }

    /**
     * @todo 用户今日签到情况
     * @return mixed
     */
    public function showTodaySignStatus()
    {
        $today = Carbon::today()->format('Y-m-d');
        $lover_id = $this->user->lover->lover_id;
        $data = [
            'user_id'       => $this->user->id,
            'sign_status'   => UserSign::NOT_SIGN,
            'sign_date'     => $today,
            'kiss_status'   => 0,
            'hug_status'   => 0,
            'lover_sign'    => [
                'user_id'       => $lover_id,
                'sign_status'   => UserSign::NOT_SIGN,
                'sign_date'     => $today,
                'kiss_status'   => 0,
                'hug_status'   => 0,
            ]
        ];
        $signs = UserSign::whereIn('user_id', [$this->user->id, $lover_id])
            ->where('sign_date', '=', $today)
            ->field(['user_id', 'sign_date', 'sign_status', 'kiss_status', 'hug_status'])
            ->select();
        foreach ($signs as $sign){
            if ($sign->user_id == $this->user->id){
                $data['sign_status'] = $sign->sign_status;
                $data['kiss_status'] = $sign->kiss_status;
                $data['hug_status'] = $sign->hug_status;
            }
            if ($sign->user_id == $lover_id){
                $data['lover_sign']['sign_status'] = $sign->sign_status;
                $data['lover_sign']['kiss_status'] = $sign->kiss_status;
                $data['lover_sign']['hug_status'] = $sign->hug_status;
            }
        }
        return $data;
    }

    /**
     * @todo 查询月签到情况
     * 临时变动，代码渣渣，有空不改
     * @param Request $request
     * @return mixed
     */
    public function queryMonthSignData(Request $request)
    {
        $year = $request->get('year', Carbon::today()->year);
        $month = $request->get('month', Carbon::today()->format('m'));
        // 查询缓存数据
        $data = UserSignCache::getUserMonthSignData($this->user->id, $year . '-' . $month);
        if ($data) return $data;
        // 无缓存数据，读取数据库数据
        $lover_id = $this->user->lover->lover_id;
        $signs = UserSign::whereIn('user_id', [$this->user->id, $lover_id])
            ->whereMonth('sign_date', $year . '-' . $month)
            ->field(['id', 'user_id', 'sign_date', 'sign_status'])
            ->order('sign_date')
            ->select();
        $sign_group_by_date = [];
        foreach ($signs as $sign){
            $sign_group_by_date[$sign->sign_date][] = $sign->toArray();
        }
        $data = [];
        foreach ($sign_group_by_date as $date => $sign){
            if (count($sign) == 2){
                $data[] = [
                    'sign_date' => $date,
                    'sign_status' => 3
                ]; //双人签到
                continue;
            }
            if ($sign[0]['user_id'] == $lover_id){
                $data[] = [
                    'sign_date' => $date,
                    'sign_status' => 2
                ];
                continue;
            }
            $data[] = [
                'sign_date' => $date,
                'sign_status' => 1
            ];
        }
        UserSignCache::setUserMonthSignData($this->user->id, $year . '-' . $month, $data);
        return $data;
    }
}
